/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.common.reflect;


import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * 反射 方法参数
 * <p/>
 * <pre>
 *     Parameter.lookup(method)[0].canAcceptType(Object.class);
 * </pre>
 * <p/>
 * 如果需要获取方法名称可以借助
 * 1. javassist 发现有时获取有问题,也不知道是使用问题
 * 2. Paranamer 轻量,专门获取名称只有几个类 (https://github.com/paul-hammant/paranamer)
 * <pre>
 *     CachingParanamer paranamer = new CachingParanamer(new AdaptiveParanamer(
 *        new AnnotationParanamer(),
 *        // 默认读取类中public static final _PARAMETER_DATA 可以提高效率, 这个可以通过 maven 插件生成
 *        new DefaultParanamer(),
 *        new BytecodeReadingParanamer()
 *     ));
 *     Method[] methods = Parameter.class.getDeclaredMethods();
 *     for (Method method : methods) {
 *          String[] names = paranamer.lookupParameterNames(method);
 *          System.out.println(method.getName() + ", param names: " + Arrays.toString(names));
 *     }
 * </pre>
 *
 * @author vacoor
 */
public class Parameter implements AnnotatedElement {
    private final Method method;
    private final int index;
    private final Class<?> type;
    private final Type genericType;
    private final Map<Class<?>, Annotation> annotations = new HashMap<Class<?>, Annotation>();

    public static Parameter[] lookup(Method method) {
        if (method == null) {
            throw new IllegalArgumentException("method must not be null");
        }

        Class<?>[] types = method.getParameterTypes();
        Type[] genericTypes = method.getGenericParameterTypes();
        Annotation[][] annotations = method.getParameterAnnotations();

        Parameter[] params = new Parameter[types.length];
        for (int i = 0; i < types.length; i++) {
            params[i] = new Parameter(method, types[i], genericTypes[i], annotations[i], i);
        }

        return params;
    }


    private Parameter(Method method, Class<?> type, Type genericType, Annotation[] annotations, int index) {
        this.method = method;
        this.type = type;
        this.genericType = genericType;
        this.index = index;

        for (Annotation a : annotations) {
            this.annotations.put(a.annotationType(), a);
        }
    }

    public boolean canAcceptType(Class<?> type) {
        return this.type.isAssignableFrom(type);
    }

    public Method getMethod() {
        return method;
    }

    public int getIndex() {
        return index;
    }

    public Class<?> getType() {
        return type;
    }

    public Type getGenericType() {
        return genericType;
    }

    @Override
    public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        return getAnnotation(annotationClass) != null;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
        return (T) annotations.get(annotationClass);
    }

    @Override
    public Annotation[] getAnnotations() {
        Collection<Annotation> values = annotations.values();
        return values.toArray(new Annotation[values.size()]);
    }

    /**
     * 该方法调用 {@link #getAnnotations()}
     *
     * @return
     */
    @Override
    public Annotation[] getDeclaredAnnotations() {
        return getAnnotations();
    }
}
